import os
import shutil
from datetime import datetime, timedelta

import numpy as np
from numpy import deprecate


def list_files(directory, filter_func):
    file_paths = []
    for file in os.listdir(directory):
        file_path = os.path.join(directory, file)
        if filter_func(file_path):
            file_paths.append(file_path)
    return file_paths


def find_files(directory, filter_func):
    file_paths = []
    for root, dirs, files in os.walk(directory):
        for file in files:
            file_path = os.path.join(root, file)
            if filter_func(file_path):
                file_paths.append(file_path)
    return file_paths


def last_days(date_str, n):
    # 将输入字符串转换为datetime对象
    base_date = datetime.strptime(date_str, '%Y_%m_%d')

    # 直接生成从近到远的日期列表
    date_list = []
    for i in range(0, n):
        # 计算当前日期：base_date减去i天
        current_date = base_date - timedelta(days=i)
        # 将日期格式化为字符串并添加到列表开头
        date_list.insert(0, current_date.strftime('%Y_%m_%d'))

    return date_list


def find_last_files(directory, filter_func, days, today_str=None, date_format='%Y_%m_%d'):
    """
    查找指定目录下包含过去days天日期的文件，并按日期顺序返回

    参数:
    dir -- 要搜索的目录
    filter_func -- 文件过滤函数，用于检查文件扩展名等属性
    days -- 过去的天数
    date_format -- 日期字符串格式（默认'%Y_%m_%d'）
    today_str -- 基准日期字符串（默认使用当前日期）
    include_today -- 是否包含基准日期当天（默认False）

    返回:
    按日期顺序（从近到远）排列的文件路径列表
    """
    # 1. 确定基准日期
    if today_str:
        base_date = datetime.strptime(today_str, date_format)
    else:
        base_date = datetime.today()

    # 2. 生成日期列表（从近到远）
    date_list = []
    for i in range(0, days):
        date = base_date - timedelta(days=i)
        date_list.append(date.strftime(date_format))

    # 3. 创建日期到文件的映射
    date_to_files = {date: [] for date in date_list}

    # 4. 遍历目录并收集文件
    for root, dirs, files in os.walk(directory):
        for file in files:
            file_path = os.path.join(root, file)

            # 应用过滤函数
            if not filter_func(file_path):
                continue

            # 检查文件是否包含日期列表中的日期
            for date in date_list:
                if date in file_path:
                    date_to_files[date].append(file_path)
                    break  # 一个文件只关联一个日期

    # 5. 按日期顺序收集文件
    result = []
    for date in date_list:
        result.extend(date_to_files[date])

    return result


def move_file(src_file, dest_dir):
    """
    移动文件到指定目录。

    :param src_file: 源文件路径
    :param dest_dir: 目标目录路径
    """
    if not os.path.isfile(src_file):
        raise FileNotFoundError(f"源文件不存在: {src_file}")

    if not os.path.isdir(dest_dir):
        raise NotADirectoryError(f"目标目录不存在: {dest_dir}")

    # 获取文件名
    file_name = os.path.basename(src_file)
    # 构建目标文件路径
    dest_file = os.path.join(dest_dir, file_name)

    # 移动文件
    shutil.move(src_file, dest_file)
    return dest_file


def get_display_length(s):
    length = 0
    for char in s:
        if '\u4e00' <= char <= '\u9fff':  # 判断是否为中文字符
            length += 2
        else:
            length += 1
    return length


def stat_data(data, invalid_value=np.nan, title=""):
    rows, cols = data.shape[:2]
    total_values = rows * cols

    # 处理零值（包括 NaN 的情况）
    if np.isnan(invalid_value):
        invalid_count = np.sum(np.all(np.isnan(data), axis=tuple(range(2, data.ndim))))
    else:
        invalid_count = np.sum(np.all(data == invalid_value, axis=tuple(range(2, data.ndim))))

    invalid_percentage = (invalid_count / total_values) * 100 if total_values > 0 else 0
    first_valid_value = None
    first_valid_position = None
    min_value = np.nanmin(data)
    max_value = np.nanmax(data)

    # 遍历前两个维度，并在比较值时考虑其余的维度
    for i in range(rows):
        for j in range(cols):
            current_element = data[i, j]
            if np.isnan(invalid_value):
                if np.any(~np.isnan(current_element)):
                    first_valid_position = (i, j)
                    first_valid_value = current_element
                    break
            else:
                if np.any(current_element != invalid_value):
                    first_valid_position = (i, j)
                    first_valid_value = current_element
                    break
        if first_valid_value is not None:
            break

    title_length = get_display_length(title)
    padding = (80 - title_length) // 2
    title_line = f"{'=' * padding}{title}{'=' * padding}"
    print(title_line)
    print(f"Shape: {rows}, {cols}")
    print(f"Total values: {total_values}")
    print(f"Invalid values count: {invalid_count}")
    print(f"Invalid values percentage: {invalid_percentage:.2f}%")
    print(f"First valid value: {first_valid_value}")
    print(f"First valid value position: {first_valid_position}")
    print(f"Min value: {min_value}")
    print(f"Max value: {max_value}")
    print(f"{'=' * len(title_line)}")


def random_value(data):
    min_value = np.nanmin(data)
    max_value = np.nanmax(data)
    # 生成位于最小值和最大值之间的随机值
    value = np.random.uniform(min_value, max_value)
    return value


def get_long_str(df, filter_func=lambda r: True, to_str_func=lambda r: r.iloc[0], delimiter=','):
    # 筛选符合条件的行
    filtered_rows = df[df.apply(filter_func, axis=1)]

    # 将每行转换为字符串
    str_rows = filtered_rows.apply(to_str_func, axis=1)

    # 将字符串用delimiter连接在一起
    result_str = delimiter.join(str(str_row) for str_row in str_rows)

    return result_str


# 自定义插值器类
@deprecate
class PartialValidNeighborInterpolator:
    def __init__(self, grid_data):
        self.grid_data = grid_data

    def __call__(self, coords):
        x, y = coords
        x0, x1 = int(np.floor(x)), int(np.ceil(x))
        y0, y1 = int(np.floor(y)), int(np.ceil(y))

        # 检查四邻点值，并处理边界情况
        f00 = self.grid_data[x0, y0] if 0 <= x0 < self.grid_data.shape[0] and 0 <= y0 < self.grid_data.shape[
            1] else np.nan
        f01 = self.grid_data[x0, y1] if 0 <= x0 < self.grid_data.shape[0] and 0 <= y1 < self.grid_data.shape[
            1] else np.nan
        f10 = self.grid_data[x1, y0] if 0 <= x1 < self.grid_data.shape[0] and 0 <= y0 < self.grid_data.shape[
            1] else np.nan
        f11 = self.grid_data[x1, y1] if 0 <= x1 < self.grid_data.shape[0] and 0 <= y1 < self.grid_data.shape[
            1] else np.nan
        neighbors = [(f00, (x1 - x) * (y1 - y)),
                     (f10, (x - x0) * (y1 - y)),
                     (f01, (x1 - x) * (y - y0)),
                     (f11, (x - x0) * (y - y0))]

        # 计算插值，仅使用有效值
        weighted_sum = 0
        weight_total = 0
        for value, weight in neighbors:
            if not np.isnan(value):
                weighted_sum += value * weight
                weight_total += weight

        # 如果没有有效值返回 NaN
        if weight_total == 0:
            return np.nan

        return weighted_sum / weight_total
